//
// ast.c
// the ast for Potion code in-memory
//
// (c) 2008 why the lucky stiff, the freelance professor
//
#include <stdio.h>
#include <stdlib.h>
#include "potion.h"
#include "internal.h"
#include "ast.h"

// 结构 PNSource.type 索引名字。
const char *potion_ast_names[] = {
  "code", "value", "=", "not", "or", "and", "cmp", "eq", "neq",
  "gt", "gte", "lt", "lte", "pipe", "caret", "amp", "wavy", "bitl",
  "bitr", "plus", "minus", "inc", "times", "div", "rem", "pow",
  "$", "path", "query", "pathq", "expr", "table", "block", "lick",
  "proto"
};

// 结构 PNSource.type 索引 .a 项数；
//    lick 单参 双参 三参 都有，其他只有一种参数情况。
// TODO hsq 代码中 value 和 message 都只有 1 个参数，而非 3 个？
const int potion_ast_sizes[] = {
  1, 3, 2, 1, 2, 2, 2, 2, 2,
  2, 2, 2, 2, 2, 2, 2, 1, 2,
  2, 2, 2, 2, 2, 2, 2, 2,
  3, 1, 1, 1, 1, 1, 1, 3,
  2
};

// 创建结构 PNSource 对象， .a 项数为 1-3 ，固定分配 3 个。
PN potion_source(Potion *P, u8 p, PN a, PN b, PN c) {
  vPN(Source) t = PN_ALLOC_N(PN_TSOURCE, struct PNSource, 3 * sizeof(PN));
  PN_AST_ARG(t, 0) = PN_AST_ARG(t, 1) = PN_AST_ARG(t, 2) = 0;
    // TODO: potion_ast_sizes[p] * sizeof(PN) (then fix gc_copy)

  PN_AST_TYPE(t) = p;
  PN_AST_ARG(t, 0) = a;
  if (potion_ast_sizes[p] > 1) PN_AST_ARG(t, 1) = b;
  if (potion_ast_sizes[p] > 2) PN_AST_ARG(t, 2) = c;
  return (PN)t;
}

// 取 PNSource 分类名。
// TODO hsq 预先捕获，直接引用？
PN_MSG(potion_source_name) {
  vPN(Source) t = (struct PNSource *)self;
  return PN_STRING(potion_ast_names[PN_AST_TYPE(t)]);
}

// 将 PNSource 字符串化，格式： "name .a[0]" | "name (.a[0] ...)" 。
// TODO hsq 格式化？如缩进、对齐等。
// TODO hsq 减少创建 PNBytes ？
//    potion_source_string_(..., vPN(Source) t)
PN_MSG(potion_source_string) {
  int i, n;
  vPN(Source) t = (struct PNSource *)self;
  PN out = potion_bytes_str(P, potion_ast_names[PN_AST_TYPE(t)]);
  n = potion_ast_sizes[PN_AST_TYPE(t)];
  // 消除 .a 末尾的 nil 。
  while (PN_IS_NIL(PN_AST_ARG(t, --n))) ;
  switch (PN_AST_TYPE(t)) {
    case AST_MESSAGE:
      potion_bytes_obj_string(P, out, PN_AST_ARG(t, 0));
      return PN_STR_B(out);
    case AST_EXPR:
      if (n == 1) {
        potion_bytes_obj_string(P, out, PN_AST_ARG(t, 0));
        return PN_STR_B(out);
      }
      break;
    default:
      break;
  }
  for (i = 0; i <= n; i++) {
    POTION_BYTES_LITERAL(out, " ");
    // TODO hsq 将括号的处理放在循环外？
    if (n > 1 && i == 0) POTION_BYTES_LITERAL(out, "(");
    potion_bytes_obj_string(P, out, PN_AST_ARG(t, i));
    if (n > 1 && i == n - 1) POTION_BYTES_LITERAL(out, ")");
  }
  return PN_STR_B(out);
}

// 注册虚表 source 的消息。
void potion_source_init(Potion *P) {
  PN src_vt = PN_VTABLE(PN_TSOURCE);
  PN_METHOD(src_vt, "name",   potion_source_name,   0);
  PN_METHOD(src_vt, "string", potion_source_string, 0);
}
